<?php
$tree=new Tree();
$action = isset($_GET["action"]) ? $_GET["action"] : exit("没有指定action") ;
if($action==="insert"){
    //http://127.0.0.1/tree/tree.php?action=insert&parent_id=0&name=node1
    $parent_id = isset($_GET["parent_id"]) ? $_GET["parent_id"] : exit("参数不全") ;
    $name = isset($_GET["name"]) ? $_GET["name"] : exit("参数不全") ;
    $tree->insert($parent_id,$name);
}
elseif($action==="move"){
    //http://127.0.0.1/tree/tree.php?action=move&id=4&to_parent_id=1
    $id = isset($_GET["id"]) ? $_GET["id"] : exit("参数不全") ;
    $to_parent_id = isset($_GET["to_parent_id"]) ? $_GET["to_parent_id"] : exit("参数不全") ;
    $tree->move($id,$to_parent_id);
}
elseif($action==="findSup"){
    //http://127.0.0.1/tree/tree.php?action=findSup&id=0&layer=3
    $id = isset($_GET["id"]) ? $_GET["id"] : exit("参数不全") ;
    $layer = isset($_GET["layer"]) ? $_GET["layer"] : exit("参数不全") ;
    $tree->findSup($id,$layer);
}
elseif($action==="findSub"){
    //http://127.0.0.1/tree/tree.php?action=findSub&id=1&layer=6
    $id = isset($_GET["id"]) ? $_GET["id"] : exit("参数不全") ;
    $layer = isset($_GET["layer"]) ? $_GET["layer"] : exit("参数不全") ;
    $tree->findSub($id,$layer);
}

//查询上级节点和下级节点的树状图模型
class Tree{
    public $pdo;
    public function __construct(){
        $this->pdo=new PDO('mysql:host=localhost;dbname=demo', "root", "");
    }
    
    public function insert($parent_id,$name){
        if($parent_id==="0"){
            $path="0";
        }
        else{
            $parent=$this->pdo->query("SELECT * FROM `tree` WHERE `id` = $parent_id")->fetch(PDO::FETCH_ASSOC);
            if(!$parent){
                exit("找不到上级节点");
            }
            $path=$parent["path"].",".$parent_id;
        }
        $row=$this->pdo->exec("INSERT INTO `tree` (`id`, `parent_id`, `name`, `path`) VALUES (NULL, '$parent_id', '$name', '$path')");
        if($row){
            echo $row;
        }
        else{
            print_r($this->pdo->errorInfo());
        }
    }
    public function move($id,$to_parent_id){
        if($to_parent_id==="0"){
            $path="0";
        }
        else{
            $parent=$this->pdo->query("SELECT * FROM `tree` WHERE `id` = $to_parent_id")->fetch(PDO::FETCH_ASSOC);
            if(!$parent){
                exit("找不到上级节点");
            }
            $path=$parent["path"].",".$to_parent_id;
        }
        $node=$this->pdo->query("SELECT * FROM `tree` WHERE `id` = $id")->fetch(PDO::FETCH_ASSOC);
        $original_path=$node["path"];
        if(!$node){
            exit("找不到指定的节点");
        }
        
        //检查要移动的节点是不是新的上级节点的上级，避免出现逻辑错误
        if($node["id"]===$parent["id"]){
            exit("检查要移动的节点不能与新的上级节点相同");
        }
        if($original_path===$path){
            exit("要移动到的位置与原位置相同");
        }
        if(preg_match("/[^,]".$node["id"]."[,$]/",$parent["path"])){
            exit("检查要移动的节点不能是是新的上级节点的上级节点");
        }
        $updateSql="UPDATE `tree` SET `parent_id`='$to_parent_id',`path`='$path' WHERE `id` = $id;";
        $updateSql.="UPDATE `tree` SET `path`=REPLACE(`path`,'$original_path','$path') WHERE `path` LIKE '$original_path,%';";
        
        $row=$this->pdo->exec($updateSql);
        if($row){
            echo $row;
        }
        else{
            print_r($this->pdo->errorInfo());
        }
    }
    public function findSup($id,$layer){
        $node=$this->pdo->query("SELECT * FROM `tree` WHERE `id` = $id")->fetch(PDO::FETCH_ASSOC);
        if(!$node){
            exit("找不到节点");
        }
        $set=$node["path"].",$id";
        $layer++;
        $data=$this->pdo->query("SELECT * FROM `tree` WHERE `id` in ($set) order by `id` desc limit $layer")->fetchAll(PDO::FETCH_ASSOC);
        header("Content-type:application/json");
        echo json_encode($data);
    }
    protected function findSubRecursion($node,$layer){
        $layer--;
        $id=$node["id"];
        $subList=$this->pdo->query("SELECT * FROM `tree` WHERE `parent_id` = $id")->fetchAll(PDO::FETCH_ASSOC);
        if($subList){
            foreach($subList as &$sub){
                if($layer>0){
                    $sub=$this->findSubRecursion($sub,$layer);
                }
            }
            $node["children"]=$subList; 
        }
        return $node;
    }
    public function findSub($id,$layer){
        $node=$this->pdo->query("SELECT * FROM `tree` WHERE `id` = $id")->fetch(PDO::FETCH_ASSOC);
        if(!$node){
            exit("找不到节点");
        }
        $node=$this->findSubRecursion($node,$layer);
        header("Content-type:application/json");
        echo json_encode($node);
    }
}

class ContainNamePathTree extends Tree{
    protected function doFindSup($id,$layer){
        $node=$this->pdo->query("SELECT * FROM `tree` WHERE `id` = $id")->fetch(PDO::FETCH_ASSOC);
        if(!$node){
            exit("找不到节点");
        }
        $set=$node["path"].",$id";
        $layer++;
        $data=$this->pdo->query("SELECT * FROM `tree` WHERE `id` in ($set) order by `id` limit $layer")->fetchAll(PDO::FETCH_ASSOC);

        $name_list=[];
        foreach($data as &$item){
            $name_list[]=$item["name"];
            $item["name_path"]="/".join("/",$name_list);
        }
        return $data;
    }
    public function findSup($id,$layer){
        $data=$this->doFindSup($id,$layer);
        header("Content-type:application/json");
        echo json_encode($data);
    }
    protected function findSubRecursion($node,$layer){
        $layer--;
        $id=$node["id"];
        $subList=$this->pdo->query("SELECT * FROM `tree` WHERE `parent_id` = $id")->fetchAll(PDO::FETCH_ASSOC);
        if($subList){
            foreach($subList as &$sub){
                $sub["name_path"]=$node["name_path"]."/".$sub["name"];
                if($layer>0){
                    $sub=$this->findSubRecursion($sub,$layer);
                }
            }
            $node["children"]=$subList;
        }
        return $node;
    }
    public function findSub($id,$layer){
        $data=$this->doFindSup($id,$layer);
        $node=$data[count($data)-1];
        $node=$this->findSubRecursion($node,$layer);
        header("Content-type:application/json");
        echo json_encode($node);
    }
}